﻿; (function ($, window, document, undefined) {
    var mvc = window.mvc = new function () {
        var app = null;

        function isString(input) {
            return typeof (input) == 'string';
        }

        function isFunction(func) {
            var myType = {};

            return func && myType.toString.call(func) === '[object Function]';
        }

        var urlParameter = { optional: 0 };

        var routes = new function () {
            var routeTable = [];
            var routeParser = new function () {
                return {
                    parse: function (args) {
                        //todo: create a real url parser
                        var hash = args.hash;

                        if (hash == "") {
                            return {
                                controllerName: "home",
                                actionName: "index",
                                parameters: null
                            };
                        }

                        var urlParts = hash.split('/');

                        if (urlParts.length == 0)
                            urlParts.push("home");

                        if (urlParts.length == 1)
                            urlParts.push("index");

                        if (urlParts.length == 2)
                            urlParts.push(null);

                        return {
                            controllerName: urlParts[0],
                            actionName: urlParts[1],
                            parameters: { id: urlParts[2] }
                        };
                    }
                };
            };

            var mapRoute = function (name, path, defaults) {
                routeTable[name] = { name: name, path: path, defaults: defaults };
            };

            var getRouteData = function (args) {
                var routeData = routeParser.parse(args);

                return routeData;
            };

            var currentView = null;

            var onLocationChanged = function (args) {
                if (currentView != null && isFunction(currentView.view.destroy)) {
                    currentView.view.destroy();
                }

                var routeData = getRouteData(args);
                var controllerName = routeData.controllerName;
                var actionName = routeData.actionName;
                var controller = mvc.resolve(app.controllers[controllerName + "Controller"]);
                var viewResult = controller[actionName](routeData.parameters);
                var result = viewResult.execute(routeData);
                var elementSelector = app.views.renderTo;

                $(elementSelector).html(result.html);

                currentView = result;

                if (isFunction(result.view.init)) {
                    result.view.init();
                }
            };

            var hash = null;
            var timer = null;

            var checkHash = function () {
                if (hash == window.location.hash.substr(1))
                    return;

                if (timer != null)
                    clearTimeout(timer);

                hash = window.location.hash.substr(1);

                try {
                    onLocationChanged({ hash: hash });
                } finally {
                    timer = setInterval(checkHash, 100);
                }
            };

            return {
                mapRoute: mapRoute,
                init: function () {
                    if ("onhashchange" in window) {
                        $(window).bind('hashchange', function () {
                            onLocationChanged({ hash: window.location.hash.substr(1) });
                        });

                        onLocationChanged({ hash: window.location.hash.substr(1) });

                        return;
                    }

                    checkHash();
                }
            };
        };

        var viewResult = Class.extend({
            init: function (model) {
                this.model = model;
            },
            execute: function (routeData) {
                var controllerName = routeData.controllerName;
                var actionName = routeData.actionName;
                var view = app.views[controllerName][actionName];

                if (isFunction(view))
                    view = view();

                if (isString(view))
                    view = { template: view };

                if (view == undefined)
                    throw "View not found: " + controllerName + "/" + actionName;

                var html = myApp.templateParser.parse({ template: view.template, model: this.model });

                return {
                    view: view,
                    html: html,
                    model: this.model
                };
            }
        });

        var controller = Class.extend({
            init: function () {
            },
            view: function (model) {
                return new viewResult(model);
            }
        });

        var view = Class.extend({
            init: function () {
            },
            destroy: function () {
            },
            template: ''
        });

        return {
            app: {
                defaultApp: {
                    urlParameter: urlParameter,
                    routes: routes,
                    controllers: {
                        controller: controller
                    },
                    views: {
                        view: view
                    },
                    models: {},
                    run: function () {
                        routes.init();
                    },
                    templateParser: {
                        parse: function (result) {
                            return jazor.parse(result.template, result.model);
                        }
                    }
                },
                create: function (obj) {
                    app = $.extend({}, this.defaultApp, obj);

                    return app;
                }
            },
            define: function (obj, baseObj) {
                var myObj = null;

                if (baseObj != undefined) {
                    obj = baseObj.extend(obj());
                }

                return function () {
                    if (myObj != null)
                        return myObj;

                    myObj = new obj();

                    return myObj;
                };
            },
            resolve: function (namespace) {
                return isFunction(namespace) ? namespace() : namespace;
            }
        };
    };
})(jQuery, window, document);